///////////////////////////////////////////////////////////////
//  Copyright 2012 John Maddock. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
//
// Comparison operators for cpp_int_backend:
//
#ifndef BOOST_MP_CPP_INT_ADD_HPP
#define BOOST_MP_CPP_INT_ADD_HPP

#include <nil/crypto3/multiprecision/detail/constexpr.hpp>
#include <nil/crypto3/multiprecision/cpp_int/add_unsigned.hpp>

namespace nil {
    namespace crypto3 {
        namespace multiprecision {
            namespace backends {

                //
                // As above, but for adding a single limb to a non-trivial cpp_int:
                //
                template<class CppInt1, class CppInt2>
                inline BOOST_MP_CXX14_CONSTEXPR void
                    add_unsigned(CppInt1& result,
                                 const CppInt2& a,
                                 const limb_type& o) noexcept(is_non_throwing_cpp_int<CppInt1>::value) {
                    // Addition using modular arithmetic.
                    // Nothing fancy, just let uintmax_t take the strain:
                    if (&result != &a)
                        result.resize(a.size(), a.size());
                    double_limb_type carry = o;
                    typename CppInt1::limb_pointer pr = result.limbs();
                    typename CppInt2::const_limb_pointer pa = a.limbs();
                    unsigned i = 0;
                    // Addition with carry until we either run out of digits or carry is zero:
                    for (; carry && (i < result.size()); ++i) {
                        carry += static_cast<double_limb_type>(pa[i]);
#ifdef __MSVC_RUNTIME_CHECKS
                        pr[i] = static_cast<limb_type>(carry & ~static_cast<limb_type>(0));
#else
                        pr[i] = static_cast<limb_type>(carry);
#endif
                        carry >>= CppInt1::limb_bits;
                    }
                    // Just copy any remaining digits:
                    if (&a != &result) {
                        std_constexpr::copy(pa + i, pa + a.size(), pr + i);
                    }
                    if (carry) {
                        // We overflowed, need to add one more limb:
                        unsigned x = result.size();
                        result.resize(x + 1, x + 1);
                        if (result.size() > x)
                            result.limbs()[x] = static_cast<limb_type>(carry);
                    }
                    result.normalize();
                    result.sign(a.sign());
                }
                //
                // And again to subtract a single limb:
                //
                template<class CppInt1, class CppInt2>
                inline BOOST_MP_CXX14_CONSTEXPR void
                    subtract_unsigned(CppInt1& result,
                                      const CppInt2& a,
                                      const limb_type& b) noexcept(is_non_throwing_cpp_int<CppInt1>::value) {
                    // Subtract one limb.
                    // Nothing fancy, just let uintmax_t take the strain:
                    constexpr double_limb_type borrow = static_cast<double_limb_type>(CppInt1::max_limb_value) + 1;
                    result.resize(a.size(), a.size());
                    typename CppInt1::limb_pointer pr = result.limbs();
                    typename CppInt2::const_limb_pointer pa = a.limbs();
                    if (*pa >= b) {
                        *pr = *pa - b;
                        if (&result != &a) {
                            std_constexpr::copy(pa + 1, pa + a.size(), pr + 1);
                            result.sign(a.sign());
                        } else if ((result.size() == 1) && (*pr == 0)) {
                            result.sign(false);    // zero is unsigned.
                        }
                    } else if (result.size() == 1) {
                        *pr = b - *pa;
                        result.sign(!a.sign());
                    } else {
                        *pr = static_cast<limb_type>((borrow + *pa) - b);
                        unsigned i = 1;
                        while (!pa[i]) {
                            pr[i] = CppInt1::max_limb_value;
                            ++i;
                        }
                        pr[i] = pa[i] - 1;
                        if (&result != &a) {
                            ++i;
                            std_constexpr::copy(pa + i, pa + a.size(), pr + i);
                        }
                        result.normalize();
                        result.sign(a.sign());
                    }
                }

                //
                // Now the actual functions called by the front end, all of which forward to one of the above:
                //
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1,
                         unsigned MinBits2,
                         unsigned MaxBits2,
                         cpp_integer_type SignType2,
                         cpp_int_check_type Checked2,
                         class Allocator2>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    !is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    !is_trivial_cpp_int<cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>>::value>::
                    type
                    eval_add(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                             const cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>&
                                 o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                      MaxBits1,
                                                                                      SignType1,
                                                                                      Checked1,
                                                                                      Allocator1>>::value)) {
                    eval_add(result, result, o);
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1,
                         unsigned MinBits2,
                         unsigned MaxBits2,
                         cpp_integer_type SignType2,
                         cpp_int_check_type Checked2,
                         class Allocator2,
                         unsigned MinBits3,
                         unsigned MaxBits3,
                         cpp_integer_type SignType3,
                         cpp_int_check_type Checked3,
                         class Allocator3>
                inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    !is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    !is_trivial_cpp_int<cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>>::value &&
                    !is_trivial_cpp_int<cpp_int_backend<MinBits3, MaxBits3, SignType3, Checked3, Allocator3>>::value>::
                    type
                    eval_add(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                             const cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>& a,
                             const cpp_int_backend<MinBits3, MaxBits3, SignType3, Checked3, Allocator3>&
                                 b) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                      MaxBits1,
                                                                                      SignType1,
                                                                                      Checked1,
                                                                                      Allocator1>>::value)) {
                    if (a.sign() != b.sign()) {
                        subtract_unsigned(result, a, b);
                        return;
                    }
                    add_unsigned(result, a, b);
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<!is_trivial_cpp_int<
                    cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value>::type
                    eval_add(
                        cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                        const limb_type& o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                              MaxBits1,
                                                                                              SignType1,
                                                                                              Checked1,
                                                                                              Allocator1>>::value)) {
                    if (result.sign()) {
                        subtract_unsigned(result, result, o);
                    } else
                        add_unsigned(result, result, o);
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1,
                         unsigned MinBits2,
                         unsigned MaxBits2,
                         cpp_integer_type SignType2,
                         cpp_int_check_type Checked2,
                         class Allocator2>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    !is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    !is_trivial_cpp_int<cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>>::value>::
                    type
                    eval_add(
                        cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                        const cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>& a,
                        const limb_type& o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                              MaxBits1,
                                                                                              SignType1,
                                                                                              Checked1,
                                                                                              Allocator1>>::value)) {
                    if (a.sign()) {
                        subtract_unsigned(result, a, o);
                    } else
                        add_unsigned(result, a, o);
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<!is_trivial_cpp_int<
                    cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value>::type
                    eval_add(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                             const signed_limb_type&
                                 o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                      MaxBits1,
                                                                                      SignType1,
                                                                                      Checked1,
                                                                                      Allocator1>>::value)) {
                    if (o < 0)
                        eval_subtract(result,
                                      static_cast<limb_type>(nil::crypto3::multiprecision::detail::unsigned_abs(o)));
                    else if (o > 0)
                        eval_add(result, static_cast<limb_type>(o));
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1,
                         unsigned MinBits2,
                         unsigned MaxBits2,
                         cpp_integer_type SignType2,
                         cpp_int_check_type Checked2,
                         class Allocator2>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    !is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    !is_trivial_cpp_int<cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>>::value>::
                    type
                    eval_add(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                             const cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>& a,
                             const signed_limb_type&
                                 o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                      MaxBits1,
                                                                                      SignType1,
                                                                                      Checked1,
                                                                                      Allocator1>>::value)) {
                    if (o < 0)
                        eval_subtract(
                            result, a, static_cast<limb_type>(nil::crypto3::multiprecision::detail::unsigned_abs(o)));
                    else if (o > 0)
                        eval_add(result, a, static_cast<limb_type>(o));
                    else if (&result != &a)
                        result = a;
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<!is_trivial_cpp_int<
                    cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value>::type
                    eval_subtract(
                        cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                        const limb_type& o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                              MaxBits1,
                                                                                              SignType1,
                                                                                              Checked1,
                                                                                              Allocator1>>::value)) {
                    if (result.sign()) {
                        add_unsigned(result, result, o);
                    } else
                        subtract_unsigned(result, result, o);
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1,
                         unsigned MinBits2,
                         unsigned MaxBits2,
                         cpp_integer_type SignType2,
                         cpp_int_check_type Checked2,
                         class Allocator2>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    !is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    !is_trivial_cpp_int<cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>>::value>::
                    type
                    eval_subtract(
                        cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                        const cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>& a,
                        const limb_type& o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                              MaxBits1,
                                                                                              SignType1,
                                                                                              Checked1,
                                                                                              Allocator1>>::value)) {
                    if (a.sign()) {
                        add_unsigned(result, a, o);
                    } else {
                        subtract_unsigned(result, a, o);
                    }
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<!is_trivial_cpp_int<
                    cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value>::type
                    eval_subtract(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                                  const signed_limb_type&
                                      o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                           MaxBits1,
                                                                                           SignType1,
                                                                                           Checked1,
                                                                                           Allocator1>>::value)) {
                    if (o) {
                        if (o < 0)
                            eval_add(result,
                                     static_cast<limb_type>(nil::crypto3::multiprecision::detail::unsigned_abs(o)));
                        else
                            eval_subtract(result, static_cast<limb_type>(o));
                    }
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1,
                         unsigned MinBits2,
                         unsigned MaxBits2,
                         cpp_integer_type SignType2,
                         cpp_int_check_type Checked2,
                         class Allocator2>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    !is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    !is_trivial_cpp_int<cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>>::value>::
                    type
                    eval_subtract(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                                  const cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>& a,
                                  const signed_limb_type&
                                      o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                           MaxBits1,
                                                                                           SignType1,
                                                                                           Checked1,
                                                                                           Allocator1>>::value)) {
                    if (o) {
                        if (o < 0)
                            eval_add(result,
                                     a,
                                     static_cast<limb_type>(nil::crypto3::multiprecision::detail::unsigned_abs(o)));
                        else
                            eval_subtract(result, a, static_cast<limb_type>(o));
                    } else if (&result != &a)
                        result = a;
                }

                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<!is_trivial_cpp_int<
                    cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value>::type
                    eval_increment(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>&
                                       result) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                                 MaxBits1,
                                                                                                 SignType1,
                                                                                                 Checked1,
                                                                                                 Allocator1>>::value)) {
                    constexpr const limb_type one = 1;

                    if (!result.sign() &&
                        (result.limbs()[0] <
                         cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::max_limb_value))
                        ++result.limbs()[0];
                    else if (result.sign() && result.limbs()[0]) {
                        --result.limbs()[0];
                        if (!result.limbs()[0] && (result.size() == 1))
                            result.sign(false);
                    } else
                        eval_add(result, one);
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<!is_trivial_cpp_int<
                    cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value>::type
                    eval_decrement(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>&
                                       result) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                                 MaxBits1,
                                                                                                 SignType1,
                                                                                                 Checked1,
                                                                                                 Allocator1>>::value)) {
                    constexpr const limb_type one = 1;

                    if (!result.sign() && result.limbs()[0])
                        --result.limbs()[0];
                    else if (result.sign() &&
                             (result.limbs()[0] <
                              cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::max_limb_value))
                        ++result.limbs()[0];
                    else
                        eval_subtract(result, one);
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1,
                         unsigned MinBits2,
                         unsigned MaxBits2,
                         cpp_integer_type SignType2,
                         cpp_int_check_type Checked2,
                         class Allocator2>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    !is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    !is_trivial_cpp_int<cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>>::value>::
                    type
                    eval_subtract(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                                  const cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>&
                                      o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                           MaxBits1,
                                                                                           SignType1,
                                                                                           Checked1,
                                                                                           Allocator1>>::value)) {
                    eval_subtract(result, result, o);
                }
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1,
                         unsigned MinBits2,
                         unsigned MaxBits2,
                         cpp_integer_type SignType2,
                         cpp_int_check_type Checked2,
                         class Allocator2,
                         unsigned MinBits3,
                         unsigned MaxBits3,
                         cpp_integer_type SignType3,
                         cpp_int_check_type Checked3,
                         class Allocator3>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    !is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    !is_trivial_cpp_int<cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>>::value &&
                    !is_trivial_cpp_int<cpp_int_backend<MinBits3, MaxBits3, SignType3, Checked3, Allocator3>>::value>::
                    type
                    eval_subtract(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                                  const cpp_int_backend<MinBits2, MaxBits2, SignType2, Checked2, Allocator2>& a,
                                  const cpp_int_backend<MinBits3, MaxBits3, SignType3, Checked3, Allocator3>&
                                      b) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                           MaxBits1,
                                                                                           SignType1,
                                                                                           Checked1,
                                                                                           Allocator1>>::value)) {
                    if (a.sign() != b.sign()) {
                        add_unsigned(result, a, b);
                        return;
                    }
                    subtract_unsigned(result, a, b);
                }

                //
                // Simple addition and subtraction routine for trivial cpp_int's come last:
                //
                // One of the arguments is signed:
                //
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1>
                inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    (is_signed_number<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value ||
                     is_signed_number<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value)>::
                    type
                    eval_add(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                             const cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>&
                                 o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                      MaxBits1,
                                                                                      SignType1,
                                                                                      Checked1,
                                                                                      Allocator1>>::value)) {
                    if (result.sign() != o.sign()) {
                        if (*o.limbs() > *result.limbs()) {
                            *result.limbs() = detail::checked_subtract(
                                *o.limbs(),
                                *result.limbs(),
                                typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::
                                    checked_type());
                            result.negate();
                        } else
                            *result.limbs() = detail::checked_subtract(
                                *result.limbs(),
                                *o.limbs(),
                                typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::
                                    checked_type());
                    } else
                        *result.limbs() = detail::checked_add(
                            *result.limbs(),
                            *o.limbs(),
                            typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::
                                checked_type());
                    result.normalize();
                }
                // Simple version for two unsigned arguments:
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    is_unsigned_number<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    is_unsigned_number<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value>::
                    type
                    eval_add(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                             const cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>&
                                 o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                      MaxBits1,
                                                                                      SignType1,
                                                                                      Checked1,
                                                                                      Allocator1>>::value)) {
                    *result.limbs() = detail::checked_add(
                        *result.limbs(),
                        *o.limbs(),
                        typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::checked_type());
                    result.normalize();
                }

                // signed subtraction:
                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1>
                inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    (is_signed_number<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value ||
                     is_signed_number<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value)>::
                    type
                    eval_subtract(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                                  const cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>&
                                      o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                           MaxBits1,
                                                                                           SignType1,
                                                                                           Checked1,
                                                                                           Allocator1>>::value)) {
                    if (result.sign() != o.sign()) {
                        *result.limbs() = detail::checked_add(
                            *result.limbs(),
                            *o.limbs(),
                            typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::
                                checked_type());
                    } else if (*result.limbs() < *o.limbs()) {
                        *result.limbs() = detail::checked_subtract(
                            *o.limbs(),
                            *result.limbs(),
                            typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::
                                checked_type());
                        result.negate();
                    } else
                        *result.limbs() = detail::checked_subtract(
                            *result.limbs(),
                            *o.limbs(),
                            typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::
                                checked_type());
                    result.normalize();
                }

                template<unsigned MinBits1,
                         unsigned MaxBits1,
                         cpp_integer_type SignType1,
                         cpp_int_check_type Checked1,
                         class Allocator1>
                BOOST_MP_FORCEINLINE BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<
                    is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    is_unsigned_number<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value &&
                    is_unsigned_number<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>>::value>::
                    type
                    eval_subtract(cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& result,
                                  const cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>&
                                      o) noexcept((is_non_throwing_cpp_int<cpp_int_backend<MinBits1,
                                                                                           MaxBits1,
                                                                                           SignType1,
                                                                                           Checked1,
                                                                                           Allocator1>>::value)) {
                    *result.limbs() = detail::checked_subtract(
                        *result.limbs(),
                        *o.limbs(),
                        typename cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>::checked_type());
                    result.normalize();
                }

            }    // namespace backends
        }        // namespace multiprecision
    }            // namespace crypto3
}    // namespace nil

#endif
